﻿using Sprout.Core.Exceptions;
using Sprout.Core.Extensions;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

namespace Sprout.Core.Utils
{
    /// <summary>
    /// Http请求帮助类
    /// </summary>
    public static class HttpHelper
    {
        private static IHttpClientFactory httpClientFactory;

        #region HttpClientFactory
        /// <summary>
        /// HttpClientFactory
        /// </summary>
        public static IHttpClientFactory HttpClientFactory
        {
            get
            {
                if (httpClientFactory == null)
                {
                    httpClientFactory = ServiceLocator.GetService<IHttpClientFactory>();
                }
                return httpClientFactory;
            }
        }
        #endregion

        #region 创建 HttpClient
        /// <summary>
        /// 创建 HttpClient
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static HttpClient CreateClient(string name = null)
        {
            if (string.IsNullOrEmpty(name))
            {
                return HttpClientFactory.CreateClient();
            }
            else
            {
                return HttpClientFactory.CreateClient(name);
            }
        }
        #endregion

        #region 组合url及参数
        /// <summary>
        /// 组合url及参数
        /// </summary>
        /// <param name="url">url地址</param>
        /// <param name="parameters">参数</param>
        /// <returns>url</returns>
        public static string UrlCombine(string url, IEnumerable<KeyValuePair<string, string>> parameters)
        {
            if (parameters != null)
            {
                StringBuilder parameterSb = new StringBuilder();
                foreach (var parameter in parameters)
                {
                    parameterSb.Append($"&{parameter.Key}={parameter.Value}");
                }
                if (url.IndexOf('?') == -1)
                {
                    url = $"{url}?{parameterSb.ToString().TrimStart('&')}";
                }
                else
                {
                    url = $"{url}{parameterSb.ToString()}";
                }
            }
            return url;
        }
        /// <summary>
        /// 获取接口请求返回值
        /// </summary>
        /// <param name="response"></param>
        /// <returns></returns>
        private static async Task<string> GetHttpResult(HttpResponseMessage response)
        {
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                throw new HttpException(response);
            }
        }
        #endregion

        #region GET

        /// <summary>
        /// Get请求
        /// </summary>
        /// <param name="url">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> GetAsync(string url, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            HttpClient client = CreateClient(clientName);
            if (contentType != null)
            {
                client.DefaultRequestHeaders.Add("ContentType", contentType);
            }
            else
            {
                client.DefaultRequestHeaders.Add("ContentType", "application/json;charset=UTF-8;");
            }
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }

            HttpResponseMessage response = await client.GetAsync(url);
            return await GetHttpResult(response);
        }

        /// <summary>
        /// GET请求
        /// </summary>
        /// <param name="url">URL</param>
        /// <param name="parameters">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> GetAsync(string url, IEnumerable<KeyValuePair<string, string>> parameters, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            url = UrlCombine(url, parameters);
            var result = await GetAsync(url, contentType, headers, clientName);
            return result;
        }

        /// <summary>
        /// GET请求
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="url">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> GetAsync<T>(string url, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await GetAsync(url, contentType, headers, clientName);
            return result.FromJsonString<T>();
        }

        /// <summary>
        /// GET请求
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="url">URL</param>
        /// <param name="parameters">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> GetAsync<T>(string url, IEnumerable<KeyValuePair<string, string>> parameters, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            url = UrlCombine(url, parameters);
            var result = await GetAsync<T>(url, contentType, headers, clientName);
            return result;
        }

        #endregion

        #region POST
        /// <summary>
        /// POST请求
        /// </summary>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> PostAsync(string url, string data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            HttpClient client = CreateClient(clientName);
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }
            using (var httpContent = new StringContent(data, Encoding.GetEncoding(encoding)))
            {
                if (mediaType != null)
                {
                    httpContent.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
                }
                else
                {
                    httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                }

                HttpResponseMessage response = await client.PostAsync(url, httpContent);
                return await GetHttpResult(response);
            }
        }

        /// <summary>
        /// POST请求
        /// </summary>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> PostAsync(string url, IEnumerable<KeyValuePair<string, object>> data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            string dataStr = data.ToJsonString();
            var result = await PostAsync(url, dataStr, mediaType, encoding, headers, clientName);
            return result;
        }

        /// <summary>
        /// POST请求
        /// </summary>
        /// <typeparam name="T">对象</typeparam>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> PostAsync<T>(string url, string data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await PostAsync(url, data, mediaType, encoding, headers, clientName);
            return result.FromJsonString<T>();
        }

        /// <summary>
        /// POST请求
        /// </summary>
        /// <typeparam name="T">对象</typeparam>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> PostAsync<T>(string url, IEnumerable<KeyValuePair<string, object>> data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await PostAsync(url, data, mediaType, encoding, headers, clientName);
            return result.FromJsonString<T>();
        }
        /// <summary>
        /// POST请求
        /// </summary>
        /// <param name="url">url</param>
        /// <param name="value">参数</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> PostAsJsonAsync(string url, object value, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var client = CreateClient(clientName);
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }
            HttpResponseMessage response = await client.PostAsJsonAsync(url, value);
            return await GetHttpResult(response);
        }

        /// <summary>
        /// POST请求
        /// </summary>
        /// <typeparam name="T">返回对象</typeparam>
        /// <param name="url">url</param>
        /// <param name="value">参数</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> PostAsJsonAsync<T>(string url, object value, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await PostAsJsonAsync(url, value, headers, clientName);
            return result.FromJsonString<T>();
        }
        #endregion

        #region PUT
        /// <summary>
        /// PUT请求
        /// </summary>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> PutAsync(string url, string data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            HttpClient client = CreateClient(clientName);
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }
            using (var httpContent = new StringContent(data, Encoding.GetEncoding(encoding)))
            {
                if (mediaType != null)
                {
                    httpContent.Headers.ContentType = new MediaTypeHeaderValue(mediaType);
                }
                else
                {
                    httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                }

                HttpResponseMessage response = await client.PutAsync(url, httpContent);
                return await GetHttpResult(response);
            }
        }

        /// <summary>
        /// PUT请求
        /// </summary>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> PutAsync(string url, IEnumerable<KeyValuePair<string, object>> data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            string dataStr = data.ToJsonString();
            var result = await PutAsync(url, dataStr, mediaType, encoding, headers, clientName);
            return result;
        }

        /// <summary>
        /// PUT请求
        /// </summary>
        /// <typeparam name="T">对象</typeparam>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> PutAsync<T>(string url, string data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await PutAsync(url, data, mediaType, encoding, headers, clientName);
            return result.FromJsonString<T>();
        }

        /// <summary>
        /// PUT请求
        /// </summary>
        /// <typeparam name="T">对象</typeparam>
        /// <param name="url">URL地址</param>
        /// <param name="data">参数</param>
        /// <param name="mediaType">媒体类型</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> PutAsync<T>(string url, IEnumerable<KeyValuePair<string, object>> data, string mediaType = null, string encoding = "utf-8", IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await PutAsync(url, data, mediaType, encoding, headers, clientName);
            return result.FromJsonString<T>();
        }
        /// <summary>
        /// PUT请求
        /// </summary>
        /// <param name="url">url</param>
        /// <param name="value">参数</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> PutAsJsonAsync(string url, object value, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var client = CreateClient(clientName);
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }
            HttpResponseMessage response = await client.PutAsJsonAsync(url, value);
            return await GetHttpResult(response);
        }

        /// <summary>
        /// PUT请求
        /// </summary>
        /// <typeparam name="T">返回对象</typeparam>
        /// <param name="url">url</param>
        /// <param name="value">参数</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> PutAsJsonAsync<T>(string url, object value, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await PutAsJsonAsync(url, value, headers, clientName);
            return result.FromJsonString<T>();
        }

        #endregion

        #region DELETE
        /// <summary>
        /// Delete请求
        /// </summary>
        /// <param name="url">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> DeleteAsync(string url, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            HttpClient client = CreateClient(clientName);
            if (contentType != null)
            {
                client.DefaultRequestHeaders.Add("ContentType", contentType);
            }
            else
            {
                client.DefaultRequestHeaders.Add("ContentType", "application/json;charset=UTF-8;");
            }
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }
            HttpResponseMessage response = await client.DeleteAsync(url);

            return await GetHttpResult(response);
        }

        /// <summary>
        /// Delete请求
        /// </summary>
        /// <param name="url">URL</param>
        /// <param name="parameters">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回值</returns>
        public static async Task<string> DeleteAsync(string url, IEnumerable<KeyValuePair<string, string>> parameters, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            url = UrlCombine(url, parameters);
            var result = await DeleteAsync(url, contentType, headers, clientName);
            return result;
        }

        /// <summary>
        /// Delete请求
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="url">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> DeleteAsync<T>(string url, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            var result = await DeleteAsync(url, contentType, headers, clientName);
            return result.FromJsonString<T>();
        }

        /// <summary>
        /// Delete请求
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="url">URL</param>
        /// <param name="parameters">URL参数</param>
        /// <param name="contentType">Content-type设置</param>
        /// <param name="headers">请求头设置</param>
        /// <param name="clientName">HttpClient名称</param>
        /// <returns>返回对象</returns>
        public static async Task<T> DeleteAsync<T>(string url, IEnumerable<KeyValuePair<string, string>> parameters, string contentType = null, IEnumerable<KeyValuePair<string, string>> headers = null, string clientName = null)
        {
            url = UrlCombine(url, parameters);
            var result = await DeleteAsync<T>(url, contentType, headers, clientName);
            return result;
        }

        #endregion

        #region 根据文件名获取 contentType
        /// <summary>
        /// 根据文件名获取 contentType
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <returns></returns>
        public static string GetContentType(string fileName)
        {
            string contentType = "application/octet-stream";

            if (Path.HasExtension(fileName))
            {
                var surfix = Path.GetExtension(fileName).ToUpper();

                if (string.IsNullOrWhiteSpace(surfix))
                    return contentType;

                if (surfix == ".323")
                    contentType = "text/h323";
                else if (surfix == ".ACX")
                    contentType = "application/internet-property-stream";
                else if (surfix == ".AI")
                    contentType = "application/postscript";
                else if (surfix == ".AIF")
                    contentType = "audio/x-aiff";
                else if (surfix == ".AIFC")
                    contentType = "audio/x-aiff";
                else if (surfix == ".AIFF")
                    contentType = "audio/x-aiff";
                else if (surfix == ".ASF")
                    contentType = "video/x-ms-asf";
                else if (surfix == ".SR")
                    contentType = "video/x-ms-asf";
                else if (surfix == ".SX")
                    contentType = "video/x-ms-asf";
                else if (surfix == ".AU")
                    contentType = "audio/basic";
                else if (surfix == ".AVI")
                    contentType = "video/x-msvideo";
                else if (surfix == ".AXS")
                    contentType = "application/olescript";
                else if (surfix == ".BAS")
                    contentType = "text/plain";
                else if (surfix == ".BCPIO")
                    contentType = "application/x-bcpio";
                else if (surfix == ".BIN")
                    contentType = "application/octet-stream";
                else if (surfix == ".BMP")
                    contentType = "image/bmp";
                else if (surfix == ".C")
                    contentType = "text/plain";
                else if (surfix == ".CAT")
                    contentType = "application/vnd.ms-pkiseccat";
                else if (surfix == ".CDF")
                    contentType = "application/x-cdf";
                else if (surfix == ".CER")
                    contentType = "application/x-x509-ca-cert";
                else if (surfix == ".CLASS")
                    contentType = "application/octet-stream";
                else if (surfix == ".CLP")
                    contentType = "application/x-msclip";
                else if (surfix == ".CMX")
                    contentType = "image/x-cmx";
                else if (surfix == ".COD")
                    contentType = "image/cis-cod";
                else if (surfix == ".CPIO")
                    contentType = "application/x-cpio";
                else if (surfix == ".CRD")
                    contentType = "application/x-mscardfile";
                else if (surfix == ".CRL")
                    contentType = "application/pkix-crl";
                else if (surfix == ".CRT")
                    contentType = "application/x-x509-ca-cert";
                else if (surfix == ".CSH")
                    contentType = "application/x-csh";
                else if (surfix == ".CSS")
                    contentType = "text/css";
                else if (surfix == ".DCR")
                    contentType = "application/x-director";
                else if (surfix == ".DER")
                    contentType = "application/x-x509-ca-cert";
                else if (surfix == ".DIR")
                    contentType = "application/x-director";
                else if (surfix == ".DLL")
                    contentType = "application/x-msdownload";
                else if (surfix == ".DMS")
                    contentType = "application/octet-stream";
                else if (surfix == ".DOC")
                    contentType = "application/msword";
                else if (surfix == ".DOT")
                    contentType = "application/msword";
                else if (surfix == ".DVI")
                    contentType = "application/x-dvi";
                else if (surfix == ".DXR")
                    contentType = "application/x-director";
                else if (surfix == ".EPS")
                    contentType = "application/postscript";
                else if (surfix == ".ETX")
                    contentType = "text/x-setext";
                else if (surfix == ".EVY")
                    contentType = "application/envoy";
                else if (surfix == ".EXE")
                    contentType = "application/octet-stream";
                else if (surfix == ".FIF")
                    contentType = "application/fractals";
                else if (surfix == ".FLR")
                    contentType = "x-world/x-vrml";
                else if (surfix == ".GIF")
                    contentType = "image/gif";
                else if (surfix == ".GTAR")
                    contentType = "application/x-gtar";
                else if (surfix == ".GZ")
                    contentType = "application/x-gzip";
                else if (surfix == ".H")
                    contentType = "text/plain";
                else if (surfix == ".HDF")
                    contentType = "application/x-hdf";
                else if (surfix == ".HLP")
                    contentType = "application/winhlp";
                else if (surfix == ".HQX")
                    contentType = "application/mac-binhex40";
                else if (surfix == ".HTA")
                    contentType = "application/hta";
                else if (surfix == ".HTC")
                    contentType = "text/x-component";
                else if (surfix == ".HTM")
                    contentType = "text/html";
                else if (surfix == ".HTML")
                    contentType = "text/html";
                else if (surfix == ".HTT")
                    contentType = "text/webviewhtml";
                else if (surfix == ".ICO")
                    contentType = "image/x-icon";
                else if (surfix == ".IEF")
                    contentType = "image/ief";
                else if (surfix == ".III")
                    contentType = "application/x-iphone";
                else if (surfix == ".INS")
                    contentType = "application/x-internet-signup";
                else if (surfix == ".ISP")
                    contentType = "application/x-internet-signup";
                else if (surfix == ".JFIF")
                    contentType = "image/pipeg";
                else if (surfix == ".JPG")
                    contentType = "image/jpeg";
                else if (surfix == ".JPEG")
                    contentType = "image/jpeg";
                else if (surfix == ".PNG")
                    contentType = "image/png";
            }
            return contentType;
        }

        #endregion
    }
}
